{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Advanced OOPs\n",
    "\n",
    "\n",
    "## Inheritance vs Association\n",
    "One big advantage of OOP is code reusability. It is possible due to the relationships between the classes and objects. OOP generally support 2 types of relationships:\n",
    "1. Inheritance (Compile Time)\n",
    "    - Generalization\n",
    "    - Realization\n",
    "2. Association (Run Time)\n",
    "    - Aggregation (HAS-A)\n",
    "    - Composition (USES-A)\n",
    "\n",
    "### Inheritance\n",
    "In OOP, `IS-A relationship` represents inheritance. This means, that the child class is a type of parent class. For example, an apple is a fruit. So you will extend fruit to get apple.\n",
    "\n",
    "**Generalization** - It is also called a parent-child relationship. In generalization, one element is a specialization of another general component. It may be substituted for it. It is mostly used to represent inheritance.\n",
    "\n",
    "**Realization** - In a realization relationship, one entity denotes some responsibility which is not implemented by itself and the other entity that implements them. This relationship is mostly found in the case of interfaces.\n",
    "\n",
    "\n",
    "### Association\n",
    "\n",
    "Association relationship is dynamic (run time) binding while inheritance is a static (compile time) binding. If you just want to reuse the code and you know that the two are not of same kind use association. For example, you cannot inherit an oven from a kitchen. A kitchen HAS-A oven. When you feel there is a natural relationship like Apple is a Fruit use inheritance.\n",
    "\n",
    "Association relationship can further be divided into two types:\n",
    "1. Aggregation\n",
    "2. Composition\n",
    "\n",
    "**Aggregation** - Aggregation means creating instances which have references to other objects. Aggregation is the other form of association and is similar to composition. In aggregation, a container object again has several references to other objects. But, Aggregation is looser than composition. The objects' life cycles are not bound to each other in aggregation. Thus, the referring object may get destroyed before/after the referenced object.\n",
    "For example, a room has a table. So you will create a class room and then in that class create an instance of type table.\n",
    "\n",
    "**Composition** - Composition is a form of association that occurs when an object's life is tightly bound to another object's life. When the main object dies (i.e., is deleted), all the objects that are associated with that object also die. This means that the referenced object is solely contained by the referring object. \n",
    "For example, Person has a Head.\n",
    "\n",
    "**For better understandability, kindly check below mentioned resources:** \n",
    "- https://realpython.com/inheritance-composition-python/\n",
    "- https://realpython.com/python-multiple-constructors/\n",
    "- https://realpython.com/build-a-python-weather-app-cli/\n",
    "- https://realpython.com/python-class-constructor/\n",
    "- https://realpython.com/courses/using-python-class-constructors/\n",
    "- https://realpython.com/dependency-management-python-poetry/\n",
    "- https://algodaily.com/lessons/association-aggregation-composition-casting\n",
    "\n",
    "![relationships](img/relationships.png)\n",
    "\n",
    "\n",
    "## SOLID Design Principles\n",
    "In software engineering, SOLID is a mnemonic acronym for five design principles intended to make object-oriented designs more understandable, flexible, and maintainable. The principles are a subset of many principles promoted by American software engineer and instructor Robert C. Martin, first introduced in his 2000 paper Design Principles and Design Patterns.\n",
    "\n",
    "The SOLID ideas are:\n",
    "- The **single-responsibility principle**: \"There should never be more than one reason for a class to change.\" In other words, every class should have only one responsibility.\n",
    "- The **open–closed principle**: \"Software entities ... should be open for extension, but closed for modification.\"\n",
    "- The **Liskov substitution principle**: \"Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.\" See also design by contract.\n",
    "- The **interface segregation principle**: \"Many client-specific interfaces are better than one general-purpose interface.\"\n",
    "- The **dependency inversion principle**: \"Depend upon abstractions, not concretions.\"\n",
    "\n",
    "The SOLID acronym was introduced later, around 2004, by Michael Feathers.\n",
    "\n",
    "Although the SOLID principles apply to any object-oriented design, they can also form a core philosophy for methodologies such as agile development or adaptive software development."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Inheritance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Parent:\n",
    "    def __init__(self, name, message):\n",
    "        self.name = name\n",
    "        self.message = message\n",
    "    def display(self):\n",
    "        print(f'Message: {self.message} \\nby {self.name}')\n",
    "\n",
    "class Child(Parent):\n",
    "    def __init__(self, name, message, age):\n",
    "        super().__init__(name, message)\n",
    "        self.age = age\n",
    "    def display(self):\n",
    "        super().display()\n",
    "        print(f'Age: {self.age}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Message: This is a parent object \n",
      "by ABC\n"
     ]
    }
   ],
   "source": [
    "p = Parent('ABC', 'This is a parent object')\n",
    "\n",
    "p.display()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Message: This is a child object \n",
      "by XYZ\n",
      "Age: 100\n"
     ]
    }
   ],
   "source": [
    "c = Child('XYZ', 'This is a child object', 100)\n",
    "\n",
    "c.display()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Association (Aggregation)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Vehicle:\n",
    "    def __init__(self, brand, model):\n",
    "        self.brand = brand\n",
    "        self.model = model\n",
    "    def display(self):\n",
    "        print(f'Brand: {self.brand} \\nModel: {self.model}')\n",
    "\n",
    "class Bike(Vehicle):\n",
    "    def __init__(self, brand, model, max_speed):\n",
    "        super().__init__(brand, model)\n",
    "        self.max_speed = max_speed\n",
    "    def display(self):\n",
    "        super().display()\n",
    "        print(f'Maximum Speed: {self.max_speed}')\n",
    "\n",
    "\n",
    "class Person:\n",
    "    bikes = []\n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "        \n",
    "    def buy_bike(self, bike):\n",
    "        self.bikes.append(bike)\n",
    "        print('Successful')\n",
    "        \n",
    "    def owned_bikes(self):\n",
    "        for bike in self.bikes:\n",
    "            bike.display()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successful\n"
     ]
    }
   ],
   "source": [
    "bike_1 = Bike('BMW', 'ZX1000', 300)\n",
    "\n",
    "person_1 = Person('Kanav', 100)\n",
    "\n",
    "person_1.buy_bike(bike_1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Brand: BMW \n",
      "Model: ZX1000\n",
      "Maximum Speed: 300\n"
     ]
    }
   ],
   "source": [
    "person_1.owned_bikes()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Association (Composition)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person:\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        \n",
    "    class Head:\n",
    "        def __init__(self):\n",
    "            self.message = \"This is a head\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
